#include <stdio.h>
#include <string.h>
#include <dos.h>
#include "mpx_supt.h"
#include "pcb.h"
#include "int_supt.h"
#include "r3_supt.h"
#include "IO_SUPT.H"
#include "TRMDRIVE.H"

extern PCB *running;
extern queue *READY_QUEUE;
extern queue *BLOCKED_QUEUE;
extern queue *SUS_BLOCKED_QUEUE;
extern queue *SUS_READY_QUEUE;

#define SYS_STACK_SIZE 2000
char sys_stack[SYS_STACK_SIZE];

unsigned short new_ss;
unsigned short new_sp;

unsigned short mpx_ss;
unsigned short mpx_sp;

extern struct IOCB IOCB1[NUM_DEVICES];

context *context_p;

params *param_p;

PCB *cop;

void interrupt dispatch()
{
    //pinsert(cop, READY_QUEUE);
	if(mpx_ss == NULL)
	{
		mpx_ss = _SS;
		mpx_sp = _SP;
    }
	cop = READY_QUEUE->front;
	if(cop !=NULL){
        removepcb(cop, READY_QUEUE);
        _SS = FP_SEG((*cop).top);
	    _SP = FP_OFF((*cop).top);
    }

	else
	{
		cop = NULL;
		_SS = mpx_ss;
		_SP = mpx_sp;
	}
}

int dispatchcmd(){

    /*PCB *pcb_n;
    char name[10];


    pcb_n = newPCB();
    sprintf(name,"Test1");
    setupPCB(pcb_n, name, APP, 5, 1024, 1);
    context_p = (context*)(*pcb_n).top;
	context_p->DS = _DS;
	context_p->ES = _ES;
	context_p->CS = FP_SEG(&test1_R3);
	context_p->IP = FP_OFF(&test1_R3);
	context_p->FLAGS = 0x200;
    pinsert(pcb_n, READY_QUEUE);

    pcb_n = newPCB();
    sprintf(name,"Test2");
    setupPCB(pcb_n, name, APP, 4, 1024, 1);
    context_p = (context*)(*pcb_n).top;
	context_p->DS = _DS;
	context_p->ES = _ES;
	context_p->CS = FP_SEG(&test2_R3);
	context_p->IP = FP_OFF(&test2_R3);
	context_p->FLAGS = 0x200;
    pinsert(pcb_n, READY_QUEUE);

    pcb_n = newPCB();
    sprintf(name,"Test3");
    setupPCB(pcb_n, name, APP, 3, 1024, 1);
    context_p = (context*)(*pcb_n).top;
	context_p->DS = _DS;
	context_p->ES = _ES;
	context_p->CS = FP_SEG(&test3_R3);
	context_p->IP = FP_OFF(&test3_R3);
	context_p->FLAGS = 0x200;
    pinsert(pcb_n, READY_QUEUE);

    pcb_n = newPCB();
    sprintf(name,"Test4");
    setupPCB(pcb_n, name, APP, 2, 1024, 1);
    context_p = (context*)(*pcb_n).top;
	context_p->DS = _DS;
	context_p->ES = _ES;
	context_p->CS = FP_SEG(&test4_R3);
	context_p->IP = FP_OFF(&test4_R3);
	context_p->FLAGS = 0x200;
    pinsert(pcb_n, READY_QUEUE);

    pcb_n = newPCB();
    sprintf(name,"Test5");
    setupPCB(pcb_n, name, APP, 1, 1024, 1);
    context_p = (context*)(*pcb_n).top;
	context_p->DS = _DS;
	context_p->ES = _ES;
	context_p->CS = FP_SEG(&test5_R3);
	context_p->IP = FP_OFF(&test5_R3);
	context_p->FLAGS = 0x200;
    pinsert(pcb_n, READY_QUEUE);

    //printf("I got to the bottom!!!!\n");*/

    dispatch();

    return 0;
}

void interrupt sys_call()
{
     int count;
     struct IOD *next_io;
     
	(*cop).top = MK_FP(_SS,_SP);
	
	new_ss = FP_SEG(sys_stack);
	new_sp = FP_OFF(sys_stack);
	new_sp += SYS_STACK_SIZE;

	_SS = new_ss;
	_SP = new_sp;

	param_p = (params *)((*cop).top + sizeof(context));
	
	trm_getc();
	
	for(count=0;count<NUM_DEVICES;count++){
       if(IOCB1[count].flag == 1 && IOCB1[count].current != NULL){
          unblockPCB(IOCB1[count].current);
          
          IOCB1[count].current = NULL;
          next_io = IOCB1[count].next;
          
          if(next_io != NULL){
             ioscheduler((*next_io).current, (count+1), (*next_io).op_code, (*next_io).buffer, (*next_io).length);
             
             IOCB1[count].next = (*next_io).next;
             free(next_io);
          }
       }
    }

	switch((*param_p).op_code){
       case IDLE:
            pinsert(cop, READY_QUEUE);
            break;
       case EXIT:
            killPCB(cop);
            break;
       case READ:
       case WRITE:
            pinsert(cop, READY_QUEUE);
            ioscheduler(cop, (*param_p).device_id, (*param_p).op_code, (*param_p).buf_addr, (*param_p).count_addr);
            break;
            }
	dispatch();
}

/*******************************************************************************
Procedure name: loadprocs()
Purpose: Actually does the loading of PCBs into the system.  Sets the processes
         to the Suspended Ready state.
Authors Names: Johnathon Trumble
Parameters: Process Name, Process Priority
Returns: 1 if failed, 0 if sucessful
Globals: none
Calls: sys_check_program, sys_req, sys_load_program, killPCB, pinsert

Change log:  10-21-08 - Johnathon wrote beginning of function.
             10-30-08 - Johnathon added user input functionality.  Removed bugs.
*******************************************************************************/

int loadprocs(char *name, char *priority) //temporary
{
	char p_msg[80];
	int p_length;
	int mem_size, offset, count, pri;
	PCB *pcb_n;

    pri = atoi(priority);

        if(findpcb(name)){
            sprintf(p_msg, "ERROR 26: Process already exists!\n\n\0");
		    p_length = strlen(p_msg);
		    sys_req(WRITE, TERMINAL, p_msg, &p_length);
            return 1;
        }
		if((count=sys_check_program("C:", name, &mem_size, &offset))!=0){
			sprintf(p_msg,"ERROR 27: Program Check Fail.\n\n\0");
			p_length = strlen(p_msg);
			sys_req(WRITE, TERMINAL, p_msg, &p_length);
            return 1;
		}
		
	    if((pcb_n = newPCB())==NULL){
            sprintf(p_msg,"ERROR 28: PCB Creation Fail.\n\n\0");
	        p_length = strlen(p_msg);
	        sys_req(WRITE, TERMINAL, p_msg, &p_length);
	        return 1;
        }

	    setupPCB(pcb_n, name, APP, pri, mem_size, offset);
	    
	    if((sys_load_program((*pcb_n).load, mem_size, "C:", name))){
            sprintf(p_msg,"ERROR 29: PCB Load Fail.\n\n\0");
	        p_length = strlen(p_msg);
	        sys_req(WRITE, TERMINAL, p_msg, &p_length);
	        killPCB(pcb_n);
	        return 1;
        }

	    context_p = (context*)(*pcb_n).top;

	    context_p->DS = _DS;
	    context_p->ES = _ES;
	    context_p->CS = FP_SEG((*pcb_n).exe);
	    context_p->IP = FP_OFF((*pcb_n).exe);
	    context_p->FLAGS = 0x200;

	    pinsert(pcb_n, SUS_READY_QUEUE);
	    (*pcb_n).state = SUS_READY;

	return 0;
}
